package config;

import entity.DemoA;
import entity.DemoB;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

/**
 * 测试配置类
 * 在上一篇中 我们已经将bean.xml中的<bean></bean>标签已经去掉
 * 交给@Component注解去管理
 * 但是我们的bean.xml文件还存在 其中配置了扫描的包路径
 * <context:component-scan base-package="service.**, dao.**"></context:component-scan>
 * 接下来我们要删除bean.xml 全盘由注解去做这些事情
 *
 * @Configuration
 *      作用：标注当前类是一个配置类， 类似之前的bean.xml
 * @ComponentScan
 *      作用：指定配置自动扫描package或者class，将符合条件的组件加入到IOC容器中
 *      属性：
 *          value：扫描指定包，使用默认扫描规则，即被@Component, @Repository, @Service, @Controller
 *                 或者已经声明过@Component自定义注解标记的组件, 数组类型可以写多个
 *          basePackageClasses：扫描指定类文件
 *          basePackages: 同value属性
 * 在之前学习的过程中 我们已经了解到@Component注解可以将符合条件的组件加入到IOC容器中
 * 但是在开发过程中 我们经常要用到第三方的jar包，此时 我们就无法使用@Component注解
 * 所以spring引入了@Bean注解，该注解可以使我们自由的将我们需要的的组件加入到IOC容器中
 * @Bean
 *      作用：告诉Spring这个方法将会返回一个对象，这个对象要注册为Spring应用上下文中的bean
 *           其中IOC容器中bean的id默认为方法名 也可以通过value或者name属性进行修改
 *      属性：
 *          value：修改存入IOC容器中的bean的id
 *          name：同value
 * 通过之前的学习我们可以知道@Component注解也可以注册Bean并装配到IOC容器
 * 那么@Component和@Bean的区别是什么
 * @Component:通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
 * 而@Bean:通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
 * 总结：@Component和@Bean都是用来注册Bean并装配到Spring容器中，
 * 但是Bean比Component的自定义性更强。可以实现一些Component实现不了的自定义加载类。
 *
 * 在实际开发中 我们会遇到很多配置文件情况 比如 spring的配置文件 springmvc的还有mybatis的等等
 * 为了方便管理 他们分属不同的文件下, 最终统一都要交给spring去管理。
 * 在我们使用xml配置文件时 可以使用<import></import>标签将所有的配置文件引入我们的主配置文件
 *  <import resource="classpath:spring/spring-*.xml"></import>
 *  这样，使我们的逻辑更加清晰，在注解中我们也可以使用@Import注解来完成这样的工作
 *
 * @Import
 *      作用：导入配置类到主配置类，统一交由spring去管理
 *      属性：
 *          value: 待导入类的class对象，数组类型，如果只导入一个类，可以不按照数组格式书写
 *          @Import(TestConfigB.class)
 * 此处我们也可以不用@Import注解，可以在要导入的类上面加上@Configuration注解也可以达到一样的效果
 * 但是这样配置文件就缺少了层次感，显得杂乱无章。如果一定要用@Configuration注解，请保证该类可以被
 * spring扫描到， 即新增config包 @ComponentScan(basePackages = {"config"})
 *
 * 学到这里， 我们的bean.xml文件就可以去掉了，这是不是意味着我们就可以不需要配置文件了呢？
 * 其实并不是，在实际开发过程中还是需要有配置文件存在的 比如说连接数据库的url，username，password等等
 * 这里我们在resources目录下新增一个test.properties文件 来模拟存储连接数据库的信息，
 * 在TestConfigB类中新增一个username属性， 如下
 * private String username;
 * 此时我们如何获取properties文件中存储的数据呢？
 * 如果只有一个配置文件我们可以使用@PropertySource注解，
 * 如果有多个配置文件我们可以使用@PropertySources注解。
 * 此处我们只讲解@PropertySource注解，其实原理时一样的
 * @PropertySource
 *      作用：导入properties配置文件，配合@Value注解 可以为属性注入数据，如下
 *      @Value("${test.username}") //此处为Spring的EL表达式
 *      private String username;
 *      属性：
 *          value：要引入的properties文件的路径， 如下
 *          @PropertySource("test.properties")
 */
@Configuration
@ComponentScan(basePackages = {"service.*", "dao.*"})
/**
 * 此处的@ComponentScan(basePackages = {"config"})可以统一放到上面
 * 但是为了方便测试 我就单独写了 这是spring允许的
 */
//@ComponentScan(basePackages = {"config"})
@Import(TestConfigB.class)
@PropertySource("test.properties")
public class TestConfigA {

    /**
     * 这里为生成的bean定义了为demoA的id 可以不写
     * 默认id为方法名createDemoA
     * @return
     */
    @Bean("demoA")
    public DemoA createDemoA(){
        return new DemoA();
    }
}

/**
 * 如果这里放开@Configuration注解，请也同时放开
 * @ComponentScan(basePackages = {"config"})
 * 并且注释掉
 * @Import(TestConfigB.class)
 * 当然也可以不注释掉，但是没啥意义，而且 乱
 */
//@Configuration
class TestConfigB{

    /**
     * 使用@Value注解配合SpEL表达式注入properties中的数据
     */
    @Value("${test.username}")
    private String username;

    /**
     * 此处不再为生成的bean重新定义id 取默认值createDemoB
     * @return
     */
    @Bean
    public DemoB createDemoB(){
        System.out.println("username = " + username);
        return new DemoB();
    }
}
